package org.fsdeduplicator;

import java.io.*;

public class FileDedupe extends Thread {

    private static final String SKIPPED_PAIR_SYMBOL = "*";
    private static final String TOTAL_COUNT_SYMBOL = "$";
    private static final String STEP_SEPARATOR_SYMBOL = "!";
    private static final String SYNC_SYMBOL = "S";

    private static final int CHECK_DELAY = 100;

    private FileDedupeFrame dedupeFrame;

    private Process backend;
    private BufferedReader backendReader;
    private BufferedWriter backendWriter;

    private boolean isError = false;
    volatile private boolean normallyFinished = false;

    volatile boolean isPaused = false;

    public FileDedupe(FileDedupeFrame dedupeFrame, String rootLocation) throws IOException {
        this.dedupeFrame = dedupeFrame;

        backend = Runtime.getRuntime().exec(
                new String[]{"./fsdedupe",
                        "-r", rootLocation, "-f", "md5", "-a", "lcs",
                        "--similarity-threshold=0.8", "-t", "--size-threshold=0.8"});

        InputStream backendStdout = backend.getInputStream();
        backendReader = new BufferedReader(new InputStreamReader(backendStdout));

        OutputStream backendStdin = backend.getOutputStream();
        backendWriter = new BufferedWriter(new OutputStreamWriter(backendStdin));
    }

    public void changePausedState() {
        isPaused = !isPaused;

        if (!isPaused) {
            try {
                backendWriter.write(SYNC_SYMBOL + "\n");
                backendWriter.flush();
            } catch (IOException e) {
                isError = true;
                dedupeFrame.reportError();
            }
        }
    }

    public boolean isPaused() {
        return isPaused;
    }

    @Override
    public void run() {

        // preparing new thread for communicating with backend
        Thread ioThread = new Thread() {

            private void readFilePairs(boolean isFiltering) throws IOException {
                String line;

                // looking for total pairs count, written after '$' symbol
                while ((line = backendReader.readLine()) != null) {
                    if (isInterrupted()) {
                        return;
                    }
                    if (line.startsWith(TOTAL_COUNT_SYMBOL)) {
                        if (isFiltering) {
                            dedupeFrame.setPairsCount(Integer.parseInt(line.substring(1)), true);
                        }
                        break;
                    } else {
                        System.out.println("Backend says: " + line);
                    }
                }

                // reading file pairs
                while ((line = backendReader.readLine()) != null) {
                    if (isInterrupted()) {
                        return;
                    }

                    if (line.startsWith("\t")) {
                        dedupeFrame.addTableRow(line.substring(1).split("\t"));
                    } else if (line.startsWith(SKIPPED_PAIR_SYMBOL)) {
//                        dedupeFrame.increaseProcessedCount(true);
                    } else if (line.startsWith(SYNC_SYMBOL)) {
                        if (!isPaused) {
                            backendWriter.write(SYNC_SYMBOL + "\n");
                            backendWriter.flush();
                        }
                    } else if (line.startsWith(STEP_SEPARATOR_SYMBOL)) {
                        if (!isFiltering) {
                            normallyFinished = true;
                        }
                        return;
                    } else {
                        System.out.println("Backend says: " + line);
                    }
                }

            }

            @Override
            public void run() {
                try {
                    readFilePairs(true);
                    readFilePairs(false);

//                    if (!isInterrupted()) {
//                        throw new IOException(); // if finished not receiving end-symbol
//                    }

                } catch (IOException e) {
                    isError = true;
                    dedupeFrame.reportError();
                }
            }
        };

        ioThread.setDaemon(true);
        ioThread.start();

        while (!isInterrupted() && !isError && !normallyFinished) {
            try {
                Thread.sleep(CHECK_DELAY);
            } catch (InterruptedException e) {
                break;
            }
        }

        if (normallyFinished) {
            dedupeFrame.finish(true);
        } else {
            ioThread.interrupt();
            dedupeFrame.finish(false);
        }

        backend.destroy();
    }

    public static void main(String[] args) {
        new FileDedupeFrame();
    }
}

